package com.sxf.orm;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Properties;

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.usertype.DynamicParameterizedType;
import org.hibernate.usertype.UserType;

import com.alibaba.fastjson.JSON;

/**
 * Hibernate Json序列化
 *
 * @author shixiafeng
 */
public class ObjectJsonType implements UserType, DynamicParameterizedType,
		Serializable {

	private static final long serialVersionUID = 1L;

	public static final String CLASS_TYPE = "classType";
	public static final String TYPE = "type";

	private static final int[] SQL_TYPES = new int[] { Types.LONGVARCHAR,
			Types.CLOB, Types.BLOB };
	private int[] retSqlType = new int[] { Types.CLOB };

	private Class<?> classType;
	private int sqlType = Types.LONGVARCHAR; // before any guessing

	@Override
	public void setParameterValues(Properties params) {
		String classTypeName = params.getProperty(CLASS_TYPE);
		try {
			this.classType = ReflectHelper.classForName(classTypeName,
					this.getClass());
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			throw new HibernateException("classType not found", e);
		}
		String type = params.getProperty(TYPE);
		if (type != null) {
			this.sqlType = Integer.decode(type).intValue();
			this.retSqlType[0] = sqlType;
		}

	}

	@Override
	public Object assemble(Serializable cached, Object owner)
			throws HibernateException {
		return this.deepCopy(cached);
	}

	@Override
	public Object deepCopy(Object value) throws HibernateException {
		Object copy = null;
		if (value != null) {

			try {
				return JSON
						.parseObject(JSON.toJSONBytes(value), this.classType);
			} catch (Exception e) {
				throw new HibernateException("unable to deep copy object", e);
			}
		}
		return copy;
	}

	@Override
	public Serializable disassemble(Object value) throws HibernateException {
		try {
			return JSON.toJSONString(value);
		} catch (Exception e) {
			throw new HibernateException("unable to disassemble object", e);
		}
	}

	@Override
	public boolean equals(Object x, Object y) throws HibernateException {
		if (x == y) {
			return true;
		}
		if (x == null || y == null) {
			return false;
		}

		return x.equals(y);
	}

	@Override
	public int hashCode(Object x) throws HibernateException {
		return x.hashCode();
	}

	@Override
	public boolean isMutable() {
		return true;
	}

	@Override
	public Object nullSafeGet(ResultSet rs, String[] names,
			SessionImplementor session, Object owner)
			throws HibernateException, SQLException {
		Object obj = null;
		if (!rs.wasNull()) {
			if (this.sqlType == Types.CLOB || this.sqlType == Types.BLOB) {
				byte[] bytes = rs.getBytes(names[0]);
				if (bytes != null) {
					try {
						obj = JSON.parseObject(bytes, this.classType);
					} catch (Exception e) {
						throw new HibernateException(
								"unable to read object from result set", e);
					}
				}
			} else {
				try {
					obj = JSON.parseObject(rs.getString(names[0]),
							this.classType);
				} catch (Exception e) {
					throw new HibernateException(
							"unable to read object from result set", e);
				}
			}
		}
		return obj;
	}

	@Override
	public void nullSafeSet(PreparedStatement st, Object value, int index,
			SessionImplementor session) throws HibernateException, SQLException {
		if (value == null) {
			st.setNull(index, this.sqlType);
		} else {

			if (this.sqlType == Types.CLOB || this.sqlType == Types.BLOB) {
				try {
					st.setBytes(index, JSON.toJSONBytes(value));
				} catch (Exception e) {
					throw new HibernateException(
							"unable to set object to result set", e);
				}
			} else {
				try {
					st.setString(index, JSON.toJSONString(value));
				} catch (Exception e) {
					throw new HibernateException(
							"unable to set object to result set", e);
				}
			}
		}
	}

	@Override
	public Object replace(Object original, Object target, Object owner)
			throws HibernateException {
		return this.deepCopy(original);
	}

	@Override
	public Class<?> returnedClass() {
		return this.classType;
	}

	@Override
	public int[] sqlTypes() {
		return retSqlType;
	}
}
